package Department

import (
	"ali-hr/Database"
	"ali-hr/Models"
	"ali-hr/Services/Async"
	"ali-hr/Util"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/gookit/goutil/dump"
)

/**
 * Created by Goland
 * User: wkk alisleepy@hotmail.com
 * Time: 2021/12/21 - 00:54
 * Desc: <部门相关业务处理>
 */

// LevelMap 部门等级信息
var LevelMap map[string]string = map[string]string{
	"1": "一级部门",
	"2": "二级部门",
	"3": "三级部门",
	"4": "四级部门",
}

// ExportDepartmentPayLoad 导出部门写入队列的参数信息
type ExportDepartmentPayLoad struct {
	Name     string
	ParentId int
	LeaderId int
	Status   string
}

// List 部门列表
func List(pageIndex, pageSize int, form map[string]interface{}, all bool) ([]Models.Department, int) {
	// 定义一个切片，类型为Model.Department
	var departments []Models.Department
	var total int
	var Db = Database.SqlDB

	// 查询
	for key, value := range form {
		if value == 0 || value == "" || value == nil {
			continue
		}
		if key == "parent_id" {
			Db = Db.Where("parent_id = ?", value)
		} else if key == "leader_id" {
			Db = Db.Where("leader_id = ?", value)
		} else if key == "name" {
			Db = Db.Where("name = ?", value)
		} else if key == "status" {
			Db = Db.Where("status = ?", value)
		}
	}

	// 获取分页数据，关联Leader，查询部门列表时，关联查询staff表
	var err error
	if all == false {
		// 获取分页数据
		err = Db.Preload("Leader").
			Preload("Parent").
			Preload("Company").
			Limit(pageSize).Offset((pageIndex - 1) * pageSize).
			Find(&departments).Error
	} else {
		// 获取所有数据
		err = Db.Preload("Leader").
			Preload("Parent").
			Preload("Company").
			Find(&departments).Error
	}
	if err != nil {
		dump.P(err)
		return nil, 0
	}

	// 处理部门级别
	// 错误写法
	//for _, item := range departments {
	//	level := reflect.ValueOf(&item.Level)
	//	level.Elem().SetString("二级部门")
	//	dump.P(item.Level)
	//}
	// 正确写法
	for index, _ := range departments {
		departments[index].Level = LevelMap[departments[index].Level]
	}

	// 获取总数
	Db.Table("ali_department").Count(&total)
	return departments, total
}

// GetDepartmentInfo 查看部门信息
func GetDepartmentInfo(id int) Models.Department {
	var department Models.Department
	department.Id = uint(id)
	Database.SqlDB.First(&department)
	return department
}

// CreateDepartment 新建部门
func CreateDepartment(form map[string]interface{}) (bool, string) {
	// todo 添加事件，增加一些新增部门后的异步事件
	// todo 新增部门时，自动添加created_at字段值
	// 先获取同一个父级部门下，有没有同名的部门，有则返回失败
	parentId, _ := strconv.Atoi(fmt.Sprintf("%v", form["parent_id"]))
	var parent Models.Department
	err1 := Database.SqlDB.Where("id = ?", form["parent_id"]).Find(&parent).Error
	if parentId != 0 && err1 != nil {
		return false, "上级部门不存在"
	}
	var department Models.Department
	err2 := Database.SqlDB.Where("parent_id = ? and name = ?", parentId, form["name"]).Find(&department).Error
	if err2 == nil {
		return false, "已有同名部门存在，无法新建部门"
	}

	// 先判断下父级部门是否有效，无效部门不能创建子部门
	if parentId != 0 && parent.Status != "生效" {
		return false, "上级部门当前未生效，无法添加子部门"
	}

	// 新增部门操作
	leaderId, _ := strconv.Atoi(fmt.Sprintf("%v", form["leader_id"]))
	companyId, _ := strconv.Atoi(fmt.Sprintf("%v", form["company_id"]))
	department.Name = fmt.Sprintf("%v", form["name"])
	department.ParentId = parentId
	department.LeaderId = leaderId
	department.CompanyId = companyId
	department.Status = fmt.Sprintf("%v", form["status"])
	department.NamePath = parent.NamePath + "-" + fmt.Sprintf("%v", form["name"])
	department.Level = strconv.Itoa(getLevelByIdPath(department.IdPath))

	// 新建操作
	result := Database.SqlDB.Create(&department)
	if result.Error != nil {
		dump.P("添加部门失败，错误信息err：", result.Error)
		return false, "添加失败"
	}

	return true, "添加成功"
}

// UpdateDepartment 修改部门信息
func UpdateDepartment(id uint, form map[string]interface{}) (bool, string) {
	// todo 添加事件，同时异步去修改员工信息
	// todo 实现更新时自动更新updated_at字段
	// 先查询，如果没有则返回失败
	var department Models.Department
	err := Database.SqlDB.Where("id = ?", id).Find(&department).Error
	if err != nil {
		dump.P("该部门不存在， id = ", id)
		return false, "该部门不存在"
	}

	// 父级部门
	var parent Models.Department
	parentId, err2 := strconv.Atoi(fmt.Sprintf("%v", form["parent_id"]))
	if err2 != nil {
		dump.P("父级部门Id处理失败, parent_id = ", parentId)
		return false, "抱歉，程序错误"
	}
	err3 := Database.SqlDB.Where("id = ?", parentId).Find(&parent).Error
	if err3 != nil {
		dump.P("父级部门不存在, parent_id = ", form["parent_id"])
		return false, "父级部门不存在，更新失败"
	}

	// 其他信息，id_path和name_path，level
	idPath := parent.IdPath + "-" + strconv.Itoa(int(id))
	// 知识点，interface{} 强转为 string
	namePath := parent.NamePath + "-" + fmt.Sprintf("%v", form["name"])
	form["id_path"] = idPath
	form["name_path"] = namePath
	// 根据id_path获取level
	level := getLevelByIdPath(idPath)
	form["level"] = strconv.Itoa(level)
	form["parent_id"], _ = strconv.Atoi(fmt.Sprintf("%v", form["parent_id"]))
	form["updated_at"] = time.Now()

	if Util.Debug() {
		dump.P("form信息：", form)
	}

	// 批量更新多个字段
	// 错误写法
	// err4 := Database.SqlDB.Table("ali_department").Where("id = ?", id).Updates(form).Error
	err4 := Database.SqlDB.Model(&department).Where("id = ?", id).Updates(form).Error
	if err4 != nil {
		dump.P("更新失败，数据form = ", form)
		return false, "更新失败"
	}

	// 通过AfterUpdate hook 事件，更新子部门信息，level、id_path、name_path
	return true, "更新成功"
}

// getLevelByIdPath 根据idPath 获取 部门level
func getLevelByIdPath(idPath string) int {
	return len(strings.Split(idPath, "-"))
}

// DeleteDepartment 删除部门信息，关闭部门
func DeleteDepartment(departmentId uint) (bool, string) {
	// 校验是否部门及子部门内还有员工存在，有员工存在则无法删除
	var department Models.Department
	err1 := Database.SqlDB.Where("id = ?", departmentId).Find(&department).Error
	if err1 != nil {
		dump.P("删除部门失败，该部门不存在，id = ", departmentId)
		return false, "该部门不存在"
	}
	// 判断部门下是否有无子部门，有子部门无法删除
	if checkDepartmentHasChildren(departmentId, true) == true {
		dump.P("当前部门存在子部门， 部门ID = ", departmentId)
		return false, "抱歉，当前部门下存在子部门，无法删除当前部门"
	}

	// 判断当前部门下是否有员工
	staffCount := GetStaffNumByDepartmentId(departmentId)
	if staffCount > 0 {
		dump.P("删除部门失败，当前部门还存在非离职员工，部门ID：", departmentId)
		return false, "当前部门下还有员工，请先调整员工到其他部门再进行删除操作"
	}

	//err2 := Database.SqlDB.Model(&department).Update("status", Models.DepartmentClose).Error
	// 删除操作，带有deleted_at字段，会自动软删除
	err2 := Database.SqlDB.Delete(&department).Error
	if err2 != nil {
		dump.P("删除部门失败，部门ID：", departmentId)
		return false, "删除失败"
	}
	return true, "删除成功"
}

// CloseDepartment 封存部门，修改状态为关闭
func CloseDepartment(departmentId uint) (bool, string) {
	// 校验是否部门及子部门内还有员工存在，有员工存在则无法封存
	var department Models.Department
	err1 := Database.SqlDB.Where("id = ?", departmentId).Find(&department).Error
	if err1 != nil {
		dump.P("封存部门失败，该部门不存在，id = ", departmentId)
		return false, "该部门不存在"
	}
	// 判断部门下是否有无子部门，有子部门无法删除
	if checkDepartmentHasChildren(departmentId, true) == true {
		dump.P("当前部门存在子部门， 部门ID = ", departmentId)
		return false, "抱歉，当前部门下存在子部门，无法封存当前部门"
	}

	// 判断当前部门下是否有员工
	staffCount := GetStaffNumByDepartmentId(departmentId)
	if staffCount > 0 {
		dump.P("封存部门失败，当前部门还存在非离职员工，部门ID：", departmentId)
		return false, "当前部门下还有员工，请先调整员工到其他部门再进行封存操作"
	}

	// 封存部门，仅修改部门状态为已关闭
	err2 := Database.SqlDB.Model(&department).Update("status", Models.DepartmentClose).Error
	if err2 != nil {
		dump.P("删除部门失败，部门ID：", departmentId)
		return false, "封存失败"
	}
	return true, "封存成功"
}

// EffectDepartment 部门生效操作，未生效变为已生效
func EffectDepartment(departmentId uint) (bool, string) {
	if departmentId == 0 {
		return false, "非法参数"
	}

	var department Models.Department
	err1 := Database.SqlDB.Where("id = ?", departmentId).Find(&department).Error
	if err1 != nil {
		dump.P("生效部门操作失败，部门不存在，部门ID：", departmentId)
		dump.P("生效部门操作失败，err信息：", err1)
		return false, "该部门部门不存在"
	}

	// 生效部门操作，修改状态
	err2 := Database.SqlDB.Model(&department).Update("status", Models.DepartmentEffect).Error
	if err2 != nil {
		dump.P("部门生效失败，err信息：", err2)
		return false, "操作失败"
	}
	return true, "操作成功"
}

// AllDepartments 所有部门列表，前端组件使用
func AllDepartments() []Models.Department {
	var allDepartments []Models.Department
	err := Database.SqlDB.Select([]string{"id", "name", "name_path"}).Find(&allDepartments).Error
	if err != nil {
		dump.P("获取所有部门失败，err:", err)
		return nil
	}

	return allDepartments
}

// UpdateDepartmentChildren 更新部门的所有子部门信息
func UpdateDepartmentChildren(parent Models.Department) bool {
	// 1、获取子部门
	children := GetDepartmentChildren(parent.Id, false)
	if children == nil {
		dump.P("当前部门没有子部门，部门ID = ", parent.Id)
		return false
	}

	// 2、修改子部门的level，id_path, name_path
	for _, child := range children {
		child.IdPath = parent.IdPath + "-" + strconv.Itoa(int(child.Id))
		child.NamePath = parent.NamePath + "-" + child.Name
		child.Level = strconv.Itoa(getLevelByIdPath(child.IdPath))
		child.UpdatedAt = time.Now()
		Database.SqlDB.Save(child)

		// 通过递归更新子部门
		UpdateDepartmentChildren(child)
	}
	return false
}

// GetDepartmentChildren 获取部门的儿子部门，不包含孙子部门
func GetDepartmentChildren(departmentId uint, includeSelf bool) []Models.Department {
	if departmentId == 0 {
		return nil
	}

	var children []Models.Department
	var err error = nil
	if includeSelf == true {
		err = Database.SqlDB.Where("parent_id = ? and status != ?", departmentId, Models.DepartmentClose).
			Or("id = ? and status != ?", departmentId, Models.DepartmentClose).
			Find(&children).Error
	} else {
		err = Database.SqlDB.Where("parent_id = ? and status != ?", departmentId, Models.DepartmentClose).Find(&children).Error
	}
	if err != nil {
		dump.P("获取子部门失败，部门ID = ", departmentId)
		return nil
	}

	if len(children) == 0 {
		return nil
	}

	return children
}

// GetAllDepartmentChildren 获取部门的所有子部门，子子孙孙
func GetAllDepartmentChildren(departmentId uint, allDepartments []Models.Department) []Models.Department {
	// 1、获取子部门
	children := GetDepartmentChildren(departmentId, false)
	if children == nil {
		return allDepartments
	}

	// 2、获取子部门的子部门
	for _, child := range children {
		allDepartments = GetAllDepartmentChildren(child.Id, allDepartments)
		allDepartments = append(allDepartments, child)
	}

	return allDepartments
}

// 判断部门是否有子部门
func checkDepartmentHasChildren(departmentId uint, includeClosed bool) bool {
	// 包含已关闭的
	var count int
	if includeClosed == true {
		Database.SqlDB.Where("parent_id = ?", departmentId).Count(&count)
		if count == 0 {
			return false
		}
		return true
	}

	// 不包含已关闭的
	if includeClosed == false {
		Database.SqlDB.Where("parent_id = ? and status != ?", departmentId, Models.DepartmentClose).Count(&count)
		if count == 0 {
			return false
		}
		return true
	}

	return false
}

// GetStaffNumByDepartmentId 根据部门ID获取当前部门下在职的员工数量
func GetStaffNumByDepartmentId(departmentId uint) int {
	if departmentId == 0 {
		return 0
	}
	children := GetAllDepartmentChildren(departmentId, nil)
	if children == nil {
		return 0
	}

	var departmentIds []uint
	for _, child := range children {
		departmentIds = append(departmentIds, child.Id)
	}

	var count int
	err := Database.SqlDB.Table("ali_staff").
		Where("department_id in ?", departmentIds).
		Where("status != ?", Models.DIMISSION).
		Count(&count).Error
	if err != nil {
		return 0
	}
	return count
}

// GetDepartmentTree 部门树
func GetDepartmentTree(departmentList []Models.Department, parentId int) []Models.DepartmentTree {
	var treeList []Models.DepartmentTree
	for _, val := range departmentList {
		if val.ParentId == parentId {
			child := GetDepartmentTree(departmentList, int(val.Id))
			node := Models.DepartmentTree{
				Id:       val.Id,
				Name:     val.Name,
				ParentId: val.ParentId,
				Children: child,
				Expand:   true,
			}
			treeList = append(treeList, node)
		}
	}
	// 添加顶级节点，公司节点
	return treeList
}

// ExportDepartment 导出部门
func ExportDepartment(params map[string]interface{}) (bool, string) {
	// 写入队列
	if !Async.CreateTask("export_department", params, 0) {
		dump.P("下载部门写入队列失败")
		return false, "下载失败，请稍后再试"
	}
	return true, "开始下载，请稍后注意查收"
}
